/* @internal */
namespace ts {
    interface ActiveLabel {
        node: LabeledStatement
        name: string;
        breakTarget: FlowLabel;
        continueTarget: FlowLabel;
        referenced: boolean;
    }

    export const enum ContainerFlags {
        /** 当前节点不是容器，在重新插入该节点之前不应进行任何容器操作。 */
        None = 0,

        /** 当前节点是一个容器。 在重新插入它之前，应将其设置为当前容器（和块容器）。 当前节点没有局部变量。 示例：类、对象字面量、类型字面量、接口... */
        IsContainer = 1 << 0,

        /** 当前节点是块作用域容器。 在重新插入它之前，应将其设置为当前块容器。 示例：块（当不受函数养育时）、Catch 子句、for/for in/for 语句... */
        IsBlockScopedContainer = 1 << 1,

        /** 当前节点是控制流路径的容器。应保存和恢复当前控制流，并在容器内初始化新的控制流。 */
        IsControlFlowContainer = 1 << 2,
        /** 是函数族 */
        IsFunctionLike = 1 << 3,
        /** 是函数表达式 */
        IsFunctionExpression = 1 << 4,
        /** 具有本地 */
        HasLocals = 1 << 5,
        /** 是接口 */
        IsInterface = 1 << 6,
        是对象_ = 1 << 7,
        是结构_ = 1 << 8,
        是外部容器_ = 1 << 9,
        IsObjectLiteralOrClassExpressionMethod = 1 << 10,
    }

    export function getContainerFlags(node: Node): ContainerFlags {
        switch (node.kind) {
            case SyntaxKind.EnumDeclaration:
            case SyntaxKind.ObjectLiteralExpression:
            case SyntaxKind.TypeLiteral:
            case SyntaxKind.JSDocTypeLiteral:
                return ContainerFlags.IsContainer;
            case SyntaxKind.对象声明_:
            case SyntaxKind.结构声明_:
                return ContainerFlags.IsContainer | ContainerFlags.是对象_ | ContainerFlags.是结构_ | ContainerFlags.是外部容器_;
            case SyntaxKind.类型声明_:
                return ContainerFlags.IsContainer | ContainerFlags.是对象_;
            case SyntaxKind.InterfaceDeclaration:
                return ContainerFlags.IsContainer | ContainerFlags.IsInterface;
            case SyntaxKind.别名声明_:
                return ContainerFlags.IsContainer | ContainerFlags.HasLocals;
            case SyntaxKind.程序声明_:
                return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer;
            case SyntaxKind.SourceFile:
            case SyntaxKind.测试单元语句_:
                return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals;

            case SyntaxKind.MethodDeclaration:
            case SyntaxKind.FunctionDeclaration:
                return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.是外部容器_;

            case SyntaxKind.内部方法声明_:
            case SyntaxKind.ConstructSignature:
            case SyntaxKind.ConstructDeclaration:
            case SyntaxKind.释放声明_:
            case SyntaxKind.MethodSignature:
            case SyntaxKind.JSDocSignature:
            case SyntaxKind.JSDocFunctionType:
            case SyntaxKind.FunctionType:
                return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike;

            case SyntaxKind.FunctionExpression:
            case SyntaxKind.ArrowFunction:
                return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsFunctionExpression;

            case SyntaxKind.PropertyDeclaration:
                return (<PropertyDeclaration>node).initializer ? ContainerFlags.IsControlFlowContainer : 0;
            case SyntaxKind.CaseClause:
            case SyntaxKind.DefaultClause:
                return (<CaseClause>node).statements && (<CaseClause>node).statements.length? ContainerFlags.IsBlockScopedContainer | ContainerFlags.HasLocals : ContainerFlags.None;
            case SyntaxKind.CatchClause:
            case SyntaxKind.ForStatement:
            case SyntaxKind.ForOfStatement:
            case SyntaxKind.CaseBlock:
            case SyntaxKind.同步语句_:
                return ContainerFlags.IsBlockScopedContainer | ContainerFlags.HasLocals;
            case SyntaxKind.Block:
                return isFunctionLike(node.parent) || isforStatemenOrForOfStatemen(node.parent) || (<Block>node).版本容器块种类 ? ContainerFlags.None : ContainerFlags.IsBlockScopedContainer;
        }
        return ContainerFlags.None;
    }

    function initFlowNode<T extends FlowNode>(node: T) {
        Debug.attachFlowNodeDebugInfo(node);
        return node;
    }

    let flowNodeCreated: <T extends FlowNode>(node: T) => T = initFlowNode;

    const binder = createBinder();

    export function bindSourceFile(file: SourceFile, options: CompilerOptions) {
        performance.mark("beforeBind");
        perfLogger.logStartBindFile("" + file.fileName);
        binder(file, options);
        perfLogger.logStopBindFile();
        performance.mark("afterBind");
        performance.measure("Bind", "beforeBind", "afterBind");
    }

    function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
        let file: SourceFile;
        let parent: Node;
        let container: Node;
        let thisParentContainer: Node; // Container one level up
        let blockScopeContainer: Node;
        let lastContainer: Node;
        let delayedTypeAliases: (JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag)[];

        // state used by control flow analysis
        let currentFlow: FlowNode;
        let currentBreakTarget: FlowLabel | undefined;
        let currentContinueTarget: FlowLabel | undefined;
        let currentReturnTarget: FlowLabel | undefined;
        let currentTrueTarget: FlowLabel | undefined;
        let currentFalseTarget: FlowLabel | undefined;
        let preSwitchCaseFlow: FlowNode | undefined;
        let activeLabels: ActiveLabel[] | undefined;
        let hasExplicitReturn: boolean;
        let 外部容器节点: Node;
        let 当前版本路径: string = "";

        // state used for emit helpers
        let emitFlags: NodeFlags;

        // If this file is an external module, then it is automatically in strict-mode according to
        // ES6.  If it is not an external module, then we'll determine if it is in strict mode or
        // not depending on if we see "use strict" in certain places or if we hit a class/namespace
        // or if compiler options contain alwaysStrict.
        let inStrictMode: boolean;

        let symbolCount = 0;

        let Symbol: new (flags: SymbolFlags, name: string, 别名?: 转换词典表达式_) => Symbol;
        let classifiableNames: UnderscoreEscapedMap<true>;

        const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
        const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };


        /**
         * Inside the binder, we may create a diagnostic for an as-yet unbound node (with potentially no parent pointers, implying no accessible source file)
         * If so, the node _must_ be in the current file (as that's the only way anything could have traversed to it to yield it as the error node)
         * This version of `createDiagnosticForNode` uses the binder's context to account for this, and always yields correct diagnostics even in these situations.
         */
        function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation {
            return createDiagnosticForNodeInSourceFile(getSourceFileOfNode(node) || file, node, message, arg0, arg1, arg2);
        }

        function bindSourceFile(f: SourceFile) {
            file = f;
            inStrictMode = bindInStrictMode(file);
            classifiableNames = createUnderscoreEscapedMap<true>();
            symbolCount = 0;

            Symbol = objectAllocator.getSymbolConstructor();

            // Attach debugging information if necessary
            Debug.attachFlowNodeDebugInfo(unreachableFlow);
            Debug.attachFlowNodeDebugInfo(reportedUnreachableFlow);

            if (!file.locals) {
                bind(file);
                file.symbolCount = symbolCount;
                file.classifiableNames = classifiableNames;
                delayedBindJSDocTypedefTag();
            }

            file = undefined!;
            parent = undefined!;
            container = undefined!;
            thisParentContainer = undefined!;
            外部容器节点 = undefined!;
            blockScopeContainer = undefined!;
            lastContainer = undefined!;
            delayedTypeAliases = undefined!;
            currentFlow = undefined!;
            currentBreakTarget = undefined;
            currentContinueTarget = undefined;
            currentReturnTarget = undefined;
            currentTrueTarget = undefined;
            currentFalseTarget = undefined;
            activeLabels = undefined!;
            hasExplicitReturn = false;
            emitFlags = NodeFlags.None;
        }

        return bindSourceFile;

        function bindInStrictMode(file: SourceFile): boolean {
            if (!file.isDeclarationFile) {
                return true;
            }
            return false;
        }

        function createSymbol(flags: SymbolFlags, name: string, 别名: 转换词典表达式_ | undefined): Symbol {
            symbolCount++;
            return new Symbol(flags, name, 别名);
        }

        function addDeclarationToSymbol(symbol: Symbol, node: Declaration, symbolFlags: SymbolFlags) {
            symbol.flags |= symbolFlags;

            node.symbol = symbol;
            symbol.declarations = appendIfUnique(symbol.declarations, node);

            const name = getNameOfDeclaration(node);
            if(name){
                if(isIdentifier(name)) name.别名 = symbol.别名;
                if ((<Identifier>name).是私有的) symbol.flags |= SymbolFlags.是私有_
            }
            if (symbolFlags & (SymbolFlags.程序_ | SymbolFlags.类型声明_ | SymbolFlags.对象_ | SymbolFlags.结构_ | SymbolFlags.Interface | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral | SymbolFlags.Enum) && !symbol.members) {
                symbol.members = createSymbolTable();
            }

            if (symbolFlags & SymbolFlags.Value) {
                setValueDeclaration(symbol, node);
            }
        }

        function setValueDeclaration(symbol: Symbol, node: Declaration): void {
            const { valueDeclaration } = symbol;
            if (!valueDeclaration ||
                (isAssignmentDeclaration(valueDeclaration) && !isAssignmentDeclaration(node)) ||
                (valueDeclaration.kind !== node.kind && isEffectiveModuleDeclaration(valueDeclaration))) {
                // other kinds of value declarations take precedence over modules and assignment declarations
                symbol.valueDeclaration = node;
            }
        }

        function getDeclarationName(node: Declaration): string | undefined {    
            const name = getNameOfDeclaration(node);
            if (name) {
                return isIdentifier(name) ? name.escapedText : undefined;
            }
            switch (node.kind) {
                case SyntaxKind.程序声明_:
                    let 路径片段 = normalizePathAndParts(getDirectoryPath(file.fileName));
                    let 目录名 = lastOrUndefined(路径片段.parts)! as string
                    let name = 目录名 === '源码' ? '启动' as string : 目录名;
                    return name;
                case SyntaxKind.导入成员_:
                    return 获取省略的模块名称(<导入成员_>node);
                case SyntaxKind.ConstructSignature:
                case SyntaxKind.ConstructDeclaration:
                    return InternalSymbolName.New;
                case SyntaxKind.释放声明_:
                    return InternalSymbolName.释放_
                case SyntaxKind.FunctionType:
                case SyntaxKind.JSDocSignature:
                    return InternalSymbolName.Call;
                case SyntaxKind.JSDocFunctionType:
                    return InternalSymbolName.Call;
                case SyntaxKind.Parameter:
                    // Parameters with names are handled at the top of this function.  Parameters
                    // without names can only come from JSDocFunctionTypes.
                    Debug.assert(node.parent.kind === SyntaxKind.JSDocFunctionType, "Impossible parameter parent kind", () => `parent is: ${(ts as any).SyntaxKind ? (ts as any).SyntaxKind[node.parent.kind] : node.parent.kind}, expected JSDocFunctionType`);
                    const functionType = <JSDocFunctionType>node.parent;
                    const index = functionType.parameters.indexOf(node as ParameterDeclaration);
                    return "arg" + index as string;
            }
        }

        function 遍历祖先词典表达式(node: Node, name: string | undefined) {
            if (!name) return;
            while (true) {
                if (有转换词典节点(node)) {
                    for (let i = 0; i < node.dict?.length!; i++) {
                        const dic = node.dict![i];
                        for (let ii = 0; ii < dic.elements.length; ii++) {
                            const dice = dic.elements[ii];
                            if (isIdentifier(dice.键) && isIdentifier(dice.值)) {
                                if (name === dice.键.escapedText && name !== dice.值.escapedText) {
                                    return dice;
                                }
                            }
                        }
                    }
                }
                if (是顶级词典容器(node)) {
                    return;
                }
                if (node.parent) {
                    node = node.parent;
                }
                else {
                    return;
                }
            }
        }

        function 是顶级词典容器(node: Node) {
            switch (node.kind) {
                case SyntaxKind.程序声明_:
                case SyntaxKind.版本语句_:
                case SyntaxKind.测试单元语句_:
                case SyntaxKind.VariableDeclaration:
                case SyntaxKind.VariableStatement:
                case SyntaxKind.FunctionDeclaration:
                case SyntaxKind.MethodSignature:
                case SyntaxKind.内部方法声明_:
                case SyntaxKind.类型声明_:
                case SyntaxKind.别名声明_:
                case SyntaxKind.结构声明_:
                case SyntaxKind.对象声明_:
                    return true;
                default:
                    return false;
            }
        }

        function getDisplayName(node: Declaration): string {
            return isNamedDeclaration(node) ? declarationNameToString(node.name) : Debug.assertDefined(getDeclarationName(node));
        }

        function 验证外部容器重名(sym: Symbol, symt: SymbolTable) {
            if (外部容器节点) {
                if (isFunctionDeclaration(外部容器节点) || isMethodDeclaration(外部容器节点)) {
                    if (外部容器节点.locals && symt !== 外部容器节点.locals && 外部容器节点.locals.has(sym.escapedName)) {
                        error(sym.declarations[0], Diagnostics.声明的成员_0_与容器符号名称冲突, sym.escapedName);
                    }
                }
                else if (是对象声明(外部容器节点) && 外部容器节点.symbol.members !== symt && 外部容器节点.symbol.members?.has(sym.escapedName)) {
                    error(sym.declarations[0], Diagnostics.声明的成员_0_与容器符号名称冲突, sym.escapedName);
                }
                else if (是结构声明(外部容器节点) && 外部容器节点.symbol.members !== symt && 外部容器节点.symbol.members?.has(sym.escapedName)) {
                    error(sym.declarations[0], Diagnostics.声明的成员_0_与容器符号名称冲突, sym.escapedName);
                }
            }
        }

        function 是可以添加前缀的声明(node: Declaration) {
            switch (node.kind) {
                case SyntaxKind.FunctionDeclaration:
                case SyntaxKind.FunctionExpression:
                case SyntaxKind.InterfaceDeclaration:
                case SyntaxKind.VariableDeclaration:
                case SyntaxKind.别名声明_:
                case SyntaxKind.类型声明_:
                case SyntaxKind.导入成员_:
                case SyntaxKind.导入从成员_:
                case SyntaxKind.结构声明_:
                case SyntaxKind.对象声明_:
                case SyntaxKind.导入从声明_:
                case SyntaxKind.导入声明_:
                    return true;

            }
            return false;
        }

        /**
         * Declares a Symbol for the node and adds it to symbols. Reports errors for conflicting identifier names.
         * @param symbolTable - The symbol table which node will be added to.
         * @param parent - node's parent declaration.
         * @param node - The declaration to be added to the symbol table
         * @param includes - The SymbolFlags that node has in addition to its declaration type (eg: export, ambient, etc.)
         * @param excludes - The flags which node cannot be declared alongside in a symbol table. Used to report forbidden declarations.
         */
        function declareSymbol(symbolTable: SymbolTable, parent: Symbol | undefined, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol {
            // The exported symbol for an export default function/class node is always named "default"
            let name = getDeclarationName(node);
            const 转换词典 = 遍历祖先词典表达式(node, name);
            if (parent) {
                if (parent.escapedName === name) {
                    if (!!(parent.flags & (SymbolFlags.类型声明_ | SymbolFlags.对象_ | SymbolFlags.结构_ | SymbolFlags.Interface))) {
                        error(node, Diagnostics.声明的成员_0_与容器符号名称冲突, name);
                        name = undefined;
                    }
                }
                if (parent.别名 && 转换词典 && isIdentifier(转换词典.值) && parent.别名 === 转换词典.值.escapedText) {
                    if (!!(parent.flags & (SymbolFlags.类型声明_ | SymbolFlags.对象_ | SymbolFlags.结构_ | SymbolFlags.Interface))) {
                        error(转换词典, Diagnostics.声明的成员_0_与容器符号名称冲突, 转换词典.值.escapedText);
                        name = undefined;
                    }
                }
            }

            let symbol: Symbol | undefined;
            if (name === undefined) {
                symbol = createSymbol(SymbolFlags.None, InternalSymbolName.Missing, undefined);
            }
            else {
                // Check and see if the symbol table already has a symbol with this name.  If not,
                // create a new symbol with this name and add it to the table.  Note that we don't
                // give the new symbol any flags *yet*.  This ensures that it will not conflict
                // with the 'excludes' flags we pass in.
                //
                // If we do get an existing symbol, see if it conflicts with the new symbol we're
                // creating.  For example, a 'var' symbol and a 'class' symbol will conflict within
                // the same symbol table.  If we have a conflict, report the issue on each
                // declaration we have for this symbol, and then create a new symbol for this
                // declaration.
                //
                // Note that when properties declared in Javascript constructors
                // (marked by isReplaceableByMethod) conflict with another symbol, the property loses.
                // Always. This allows the common Javascript pattern of overwriting a prototype method
                // with an bound instance method of the same type: `this.method = this.method.bind(this)`
                //
                // If we created a new symbol, either because we didn't have a symbol with this name
                // in the symbol table, or we conflicted with an existing symbol, then just add this
                // node as the sole declaration of the new symbol.
                //
                // Otherwise, we'll be merging into a compatible existing symbol (for example when
                // you have multiple 'vars' with the same name in the same container).  In this case
                // just add this node into the declarations list of the symbol.
                let 重组版本路径名称组: string = name;
                if (当前版本路径 && 是可以添加前缀的声明(node)) {
                    重组版本路径名称组 = 当前版本路径 + "|" + name;
                }

                symbol = symbolTable.get(name);
                if (!symbol && 重组版本路径名称组 !== name) {
                    symbol = symbolTable.get(重组版本路径名称组);
                }
                if (includes & SymbolFlags.Classifiable) {
                    classifiableNames.set(name, true);
                }

                if (!symbol) {
                    symbol = createSymbol(SymbolFlags.None, name, 转换词典);
                    if (转换词典) {
                        const 名称 = (<Identifier>转换词典.值).escapedText || InternalSymbolName.Missing;
                        const 名称2 = 当前版本路径 ? 当前版本路径 + "|" + 名称 : 名称;
                        const 重复词典 = symbolTable.get(名称2);
                        if (!重复词典) {
                            const dictSymbol = createSymbol(SymbolFlags.转换词典值_, 名称, undefined);
                            dictSymbol.declarations = [转换词典];
                            dictSymbol.valueDeclaration = 转换词典;
                            symbolTable.set(名称2, dictSymbol);
                        }
                        else {
                            let message = Diagnostics.Duplicate_identifier_0;
                            forEach(重复词典.declarations, declaration => {
                                const decl = getNameOfDeclaration(declaration) || declaration;
                                const diag = createDiagnosticForNode(decl, message, getDisplayName(declaration));
                                file.bindDiagnostics.push(diag);
                            });
                            const dictSymbol = createSymbol(SymbolFlags.转换词典值_, 名称, undefined);
                            dictSymbol.declarations = [转换词典];
                            dictSymbol.valueDeclaration = 转换词典;
                        }
                    }
                    symbolTable.set(重组版本路径名称组, symbol);
                }
                else if (symbol.flags & excludes) {
                    if (isNamedDeclaration(node)) {
                        node.name.parent = node;
                    }
                    let message = symbol.flags & SymbolFlags.BlockScopedVariable
                        ? Diagnostics.Cannot_redeclare_block_scoped_variable_0
                        : Diagnostics.Duplicate_identifier_0;
                    let messageNeedsName = true;

                    if (symbol.flags & SymbolFlags.ConstEnum) {
                        message = Diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations;
                        messageNeedsName = false;
                    }

                    let multipleDefaultExports = false;

                    const declarationName = getNameOfDeclaration(node) || node;
                    const relatedInformation: DiagnosticRelatedInformation[] = [];
                    forEach(symbol.declarations, (declaration, index) => {
                        const decl = getNameOfDeclaration(declaration) || declaration;
                        const diag = createDiagnosticForNode(decl, message, messageNeedsName ? getDisplayName(declaration) : undefined);
                        file.bindDiagnostics.push(
                            multipleDefaultExports ? addRelatedInfo(diag, createDiagnosticForNode(declarationName, index === 0 ? Diagnostics.Another_export_default_is_here : Diagnostics.and_here)) : diag
                        );
                        if (multipleDefaultExports) {
                            relatedInformation.push(createDiagnosticForNode(decl, Diagnostics.The_first_export_default_is_here));
                        }
                    });
                    const diag = createDiagnosticForNode(declarationName, message, messageNeedsName ? getDisplayName(node) : undefined);
                    file.bindDiagnostics.push(multipleDefaultExports ? addRelatedInfo(diag, ...relatedInformation) : diag);
                    symbol = createSymbol(SymbolFlags.None, name, 转换词典);
                }
            }
            addDeclarationToSymbol(symbol, node, includes);
            if (symbol.parent) {
                Debug.assert(symbol.parent === parent, "Existing symbol parent should match new one");
            }
            else {
                symbol.parent = parent;
            }

            if (includes & SymbolFlags.Variable && !isParameterDeclaration(node)) {
                验证外部容器重名(symbol, symbolTable);
            }
            if (转换词典 && isIdentifier(转换词典.值) && symbol && !symbol.别名) {
                symbol.别名 = 转换词典.值.escapedText;
            }
            return symbol;
        }

        // All container nodes are kept on a linked list in declaration order. This list is used by
        // the getLocalNameOfContainer function in the type checker to validate that the local name
        // used for a container is unique.
        function bindContainer(node: Node, containerFlags: ContainerFlags) {
            // Before we recurse into a node's children, we first save the existing parent, container
            // and block-container.  Then after we pop out of processing the children, we restore
            // these saved values.
            const saveContainer = container;
            const saveThisParentContainer = thisParentContainer;
            const 储存的外部容器 = 外部容器节点;
            const savedBlockScopeContainer = blockScopeContainer;

            // Depending on what kind of node this is, we may have to adjust the current container
            // and block-container.   If the current node is a container, then it is automatically
            // considered the current block-container as well.  Also, for containers that we know
            // may contain locals, we eagerly initialize the .locals field. We do this because
            // it's highly likely that the .locals will be needed to place some child in (for example,
            // a parameter, or variable declaration).
            //
            // However, we do not proactively create the .locals for block-containers because it's
            // totally normal and common for block-containers to never actually have a block-scoped
            // variable in them.  We don't want to end up allocating an object for every 'block' we
            // run into when most of them won't be necessary.
            //
            // Finally, if this is a block-container, then we clear out any existing .locals object
            // it may contain within it.  This happens in incremental scenarios.  Because we can be
            // reusing a node from a previous compilation, that node may have had 'locals' created
            // for it.  We must clear this so we don't accidentally move any stale data forward from
            // a previous compilation.
            if(containerFlags & ContainerFlags.是外部容器_ ){
                外部容器节点 = node;
            }
            if (containerFlags & ContainerFlags.IsContainer) {
                if (node.kind !== SyntaxKind.ArrowFunction) {
                    thisParentContainer = container;
                }
                container = blockScopeContainer = node;
                if (containerFlags & ContainerFlags.HasLocals) {
                    container.locals = createSymbolTable();
                }
                addToContainerChain(container);
            }
            else if (containerFlags & ContainerFlags.IsBlockScopedContainer) {
                blockScopeContainer = node;
                blockScopeContainer.locals = undefined;
            }
            if (containerFlags & ContainerFlags.IsControlFlowContainer) {
                const saveFlowNodeCreated = flowNodeCreated;
                const saveCurrentFlow = currentFlow;
                const saveBreakTarget = currentBreakTarget;
                const saveContinueTarget = currentContinueTarget;
                const saveReturnTarget = currentReturnTarget;
                const saveActiveLabels = activeLabels;
                const saveHasExplicitReturn = hasExplicitReturn;
                const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !!getImmediatelyInvokedFunctionExpression(node);
                // A non-async, non-generator IIFE is considered part of the containing control flow. Return statements behave
                // similarly to break statements that exit to a label just past the statement body.
                if (!isIIFE) {
                    currentFlow = initFlowNode({ flags: FlowFlags.Start });
                    if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethod)) {
                        currentFlow.node = <FunctionExpression | ArrowFunction | MethodDeclaration>node;
                    }
                }
                // We create a return control flow graph for IIFEs and constructors. For constructors
                // we use the return control flow graph in strict property initialization checks.
                currentReturnTarget = isIIFE || node.kind === SyntaxKind.ConstructDeclaration ? createBranchLabel() : undefined;
                currentBreakTarget = undefined;
                currentContinueTarget = undefined;
                activeLabels = undefined;
                hasExplicitReturn = false;
                flowNodeCreated = initFlowNode;
                bindChildren(node);
                // Reset all reachability check related flags on node (for incremental scenarios)
                node.flags &= ~NodeFlags.ReachabilityAndEmitFlags;
                if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
                    node.flags |= NodeFlags.HasImplicitReturn;
                    if (hasExplicitReturn) node.flags |= NodeFlags.HasExplicitReturn;
                    (<FunctionLikeDeclaration>node).endFlowNode = currentFlow;
                }
                if (node.kind === SyntaxKind.SourceFile) {
                    node.flags |= emitFlags;
                }

                if (currentReturnTarget) {
                    addAntecedent(currentReturnTarget, currentFlow);
                    currentFlow = finishFlowLabel(currentReturnTarget);
                    if (node.kind === SyntaxKind.ConstructDeclaration) {
                        (<ConstructDeclaration>node).returnFlowNode = currentFlow;
                    }
                }
                if (!isIIFE) {
                    currentFlow = saveCurrentFlow;
                }
                currentBreakTarget = saveBreakTarget;
                currentContinueTarget = saveContinueTarget;
                currentReturnTarget = saveReturnTarget;
                activeLabels = saveActiveLabels;
                hasExplicitReturn = saveHasExplicitReturn;
                flowNodeCreated = saveFlowNodeCreated;
            }
            else if (containerFlags & ContainerFlags.IsInterface || containerFlags & ContainerFlags.是对象_ || containerFlags & ContainerFlags.是结构_) {
                bindChildren(node);                
                node.flags = node.flags & ~NodeFlags.ContainsThis;
            }
            else {
                bindChildren(node);
            }

            container = saveContainer;
            thisParentContainer = saveThisParentContainer;
            blockScopeContainer = savedBlockScopeContainer;            
            外部容器节点 = 储存的外部容器;
        }

        function 计算当前版本路径(node: Block | 导入从成员块_ | 测试单元语句_ | 导入从成员块_ | 导入成员块_) {
            if (当前版本路径 === undefined) 当前版本路径 = "";
            if (是测试单元语句(node)) {
                const id = "测试";
                当前版本路径 += ";+" + id;
                node.版本容器块种类 = 版本容器块种类_.是测试单元_;
                node.版本语句路径 = 当前版本路径;
                return;
            }
            const 版本语句 = <版本语句_ | 导入从版本成员_ | 导入版本成员_>node.parent;
            const id = 版本语句.expression.escapedText;
            if (node.版本容器块种类 === 版本容器块种类_.是版本容器块_) {
                当前版本路径 += ";+" + id;
            }
            else if (node.版本容器块种类 === 版本容器块种类_.是版本另如容器块_) {
                let index = 当前版本路径.lastIndexOf(";") + 1;
                当前版本路径 = (index === 0 ? ";" : 当前版本路径.slice(0, index)) + 生成前部反向路径(版本语句) + ",+" + id;
            }
            else if (node.版本容器块种类 === 版本容器块种类_.是版本否则容器块_) {
                let index = 当前版本路径.lastIndexOf(";") + 1;
                const 前部 = 生成前部反向路径(版本语句);
                当前版本路径 = (index === 0 ? ";" : 当前版本路径.slice(0, index)) + 前部 + (前部 ? ",-" + id : "-" + id);
            }
            node.版本语句路径 = 当前版本路径;
            const 冲突条件 = 验证版本路径是否为不可及代码(node.版本语句路径);
            if (冲突条件) {
                if (是版本语句(node.parent) && !是测试单元语句(node.parent)) {
                    error(node.parent.expression, Diagnostics.版本语句不可及_条件_0_与条件_1_不能同时为真, "+" + 冲突条件, "-" + 冲突条件);
                }
                else {
                    error(node, Diagnostics.版本语句不可及_条件_0_与条件_1_不能同时为真, "+" + 冲突条件, "-" + 冲突条件);
                }
            }
            return;
        }

        function 验证版本路径是否为不可及代码(版本路径: string): string | undefined {
            let 正条件组: Set<string> = new Set();
            let 负条件组: Set<string> = new Set();
            let 路径 = 版本路径;
            if (startsWith(路径, ";")) {
                路径 = 路径.substring(1);
            }
            const 嵌套组 = 路径.split(";");
            for (let 嵌套层 of 嵌套组) {
                const 条件组 = 嵌套层.split(",");
                for (let 条件 of 条件组) {
                    if (是正条件(条件)) {
                        条件 = 条件.substring(1);
                        if (负条件组.has(条件)) return 条件;
                        正条件组.add(条件);
                    }
                    else {
                        条件 = 条件.substring(1);
                        if (正条件组.has(条件)) return 条件;
                        负条件组.add(条件);
                    }
                }
            }
            return;
        }


        function 生成前部反向路径(node: 版本语句_ | 导入从版本成员_ | 导入版本成员_ | 测试单元语句_) {
            const 原始节点 = node;
            const ids: string[] = [];
            node = <版本语句_ | 导入从版本成员_ | 导入版本成员_ | 测试单元语句_>findAncestor(node, 是起始版本语句)!;
            提取(node);
            return ids.join(",");

            function 提取(n: 版本语句_ | 导入从版本成员_ | 导入版本成员_ | 测试单元语句_) {
                if (n !== 原始节点) {
                    ids.push("-" + (!是测试单元语句(n) ? n.expression.escapedText : "测试"))
                }
                else {
                    return;
                }
                if (!是测试单元语句(n)) {
                    if (n.elseStatement && 是版本语句(n.elseStatement)) {
                        提取(n.elseStatement);
                    }
                }
                return;
            }
        }

        function bindChildren(node: Node): void {
            bindChildrenWorker(node);
        }

        function bindEachFunctionsFirst(nodes: NodeArray<Node> | undefined): void {
            bindEach(nodes, n => n.kind === SyntaxKind.FunctionDeclaration ? bind(n) : undefined);
            bindEach(nodes, n => n.kind !== SyntaxKind.FunctionDeclaration ? bind(n) : undefined);
        }

        function bindEach(nodes: NodeArray<Node> | undefined, bindFunction: (node: Node) => void = bind): void {
            if (nodes === undefined) {
                return;
            }
            forEach(nodes, bindFunction);
        }

        function bindEachChild(node: Node) {
            forEachChild(node, bind, bindEach);
        }

        function bindChildrenWorker(node: Node): void {
            if (checkUnreachable(node)) {
                bindEachChild(node);
                bindJSDoc(node);
                return;
            }
            if (node.kind >= SyntaxKind.FirstStatement && node.kind <= SyntaxKind.LastStatement) {
                node.flowNode = currentFlow;
            }
            switch (node.kind) {
                case SyntaxKind.WhileStatement:
                    bindWhileStatement(<WhileStatement>node);
                    break;
                case SyntaxKind.ForStatement:
                    bindForStatement(<ForStatement>node);
                    break;
                case SyntaxKind.ForOfStatement:
                    bindForInOrForOfStatement(<ForInOrOfStatement>node);
                    break;
                case SyntaxKind.IfStatement:
                    bindIfStatement(<IfStatement>node);
                    break;
                case SyntaxKind.ReturnStatement:
                case SyntaxKind.ThrowStatement:
                    bindReturnOrThrow(<ReturnStatement | ThrowStatement>node);
                    break;
                case SyntaxKind.BreakStatement:
                case SyntaxKind.ContinueStatement:
                    bindBreakOrContinueStatement(<BreakOrContinueStatement>node);
                    break;
                case SyntaxKind.TryStatement:
                    bindTryStatement(<TryStatement>node);
                    break;
                case SyntaxKind.SwitchStatement:
                    bindSwitchStatement(<SwitchStatement>node);
                    break;
                case SyntaxKind.CaseBlock:
                    bindCaseBlock(<CaseBlock>node);
                    break;
                case SyntaxKind.CaseClause:
                    bindCaseClause(<CaseClause>node);
                    break;
                case SyntaxKind.ExpressionStatement:
                    bindExpressionStatement(<ExpressionStatement>node);
                    break;
                case SyntaxKind.LabeledStatement:
                    bindLabeledStatement(<LabeledStatement>node);
                    break;
                case SyntaxKind.PrefixUnaryExpression:
                    bindPrefixUnaryExpressionFlow(<PrefixUnaryExpression>node);
                    break;
                case SyntaxKind.PostfixUnaryExpression:
                    bindPostfixUnaryExpressionFlow(<PostfixUnaryExpression>node);
                    break;
                case SyntaxKind.BinaryExpression:
                    bindBinaryExpressionFlow(<BinaryExpression>node);
                    break;
                case SyntaxKind.ConditionalExpression:
                    bindConditionalExpressionFlow(<ConditionalExpression>node);
                    break;
                case SyntaxKind.VariableDeclaration:
                    bindVariableDeclarationFlow(<VariableDeclaration>node);
                    break;
                case SyntaxKind.PropertyAccessExpression:
                case SyntaxKind.ElementAccessExpression:
                    bindAccessExpressionFlow(<AccessExpression>node);
                    break;
                case SyntaxKind.CallExpression:
                    bindCallExpressionFlow(<CallExpression>node);
                    break;
                case SyntaxKind.JSDocTypedefTag:
                case SyntaxKind.JSDocCallbackTag:
                case SyntaxKind.JSDocEnumTag:
                    bindJSDocTypeAlias(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag);
                    break;
                // In source files and blocks, bind functions first to match hoisting that occurs at runtime
                case SyntaxKind.SourceFile: {
                    bindEachFunctionsFirst((node as SourceFile).statements);
                    bind((node as SourceFile).endOfFileToken);
                    break;
                }
                case SyntaxKind.Block:
                    bindEachFunctionsFirst((node as Block).statements);
                    break;
                default:
                    bindEachChild(node);
                    break;
            }
            bindJSDoc(node);
        }

        function isNarrowingExpression(expr: Expression): boolean {
            switch (expr.kind) {
                case SyntaxKind.Identifier:
                case SyntaxKind.ThisKeyword:
                case SyntaxKind.原型关键字_:
                case SyntaxKind.PropertyAccessExpression:
                case SyntaxKind.ElementAccessExpression:
                    return isNarrowableReference(expr);
                case SyntaxKind.TypeAssertionExpression:
                case SyntaxKind.类型查询表达式_:
                    return isNarrowingExpression((<TypeAssertionExpression | 类型查询表达式_>expr).expression);
                /*
                case SyntaxKind.类型判断表达式_:
                    return isNarrowingExpression((<类型判断表达式_>expr).left);
                */
                case SyntaxKind.CallExpression:
                    return hasNarrowableArgument(<CallExpression>expr);
                case SyntaxKind.ParenthesizedExpression:
                    return isNarrowingExpression((<ParenthesizedExpression>expr).expression);
                case SyntaxKind.BinaryExpression:
                    return isNarrowingBinaryExpression(<BinaryExpression>expr);
                case SyntaxKind.PrefixUnaryExpression:
                    return (<PrefixUnaryExpression>expr).operator === SyntaxKind.ExclamationToken && isNarrowingExpression((<PrefixUnaryExpression>expr).operand);
            }
            return false;
        }

        function isNarrowableReference(expr: Expression): boolean {
            return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.原型关键字_||
                (isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) ||
                isElementAccessExpression(expr) && isStringOrNumericLiteralLike(expr.argumentExpression) && isNarrowableReference(expr.expression) ||
                isOptionalChain(expr);
        }

        function hasNarrowableArgument(expr: CallExpression) {
            if (expr.arguments) {
                for (const argument of expr.arguments) {
                    if (isNarrowableReference(argument)) {
                        return true;
                    }
                }
            }
            if (expr.expression.kind === SyntaxKind.PropertyAccessExpression &&
                isNarrowableReference((<PropertyAccessExpression>expr.expression).expression)) {
                return true;
            }
            return false;
        }

        function isNarrowingBinaryExpression(expr: BinaryExpression) {
            switch (expr.operatorToken.kind) {
                case SyntaxKind.EqualsToken:
                    return isNarrowableReference(expr.left);
                case SyntaxKind.EqualsEqualsToken:
                case SyntaxKind.ExclamationEqualsToken:
                    return isNarrowableOperand(expr.left) || isNarrowableOperand(expr.right);
                case SyntaxKind.CommaToken:
                    return isNarrowingExpression(expr.right);
            }
            return false;
        }

        function isNarrowableOperand(expr: Expression): boolean {
            switch (expr.kind) {
                case SyntaxKind.ParenthesizedExpression:
                    return isNarrowableOperand((<ParenthesizedExpression>expr).expression);
                case SyntaxKind.BinaryExpression:
                    switch ((<BinaryExpression>expr).operatorToken.kind) {
                        case SyntaxKind.EqualsToken:
                            return isNarrowableOperand((<BinaryExpression>expr).left);
                        case SyntaxKind.CommaToken:
                            return isNarrowableOperand((<BinaryExpression>expr).right);
                    }
            }
            return isNarrowableReference(expr);
        }

        function createBranchLabel(): FlowLabel {
            return initFlowNode({ flags: FlowFlags.BranchLabel, antecedents: undefined });
        }

        function createLoopLabel(): FlowLabel {
            return initFlowNode({ flags: FlowFlags.LoopLabel, antecedents: undefined });
        }

        function setFlowNodeReferenced(flow: FlowNode) {
            // On first reference we set the Referenced flag, thereafter we set the Shared flag
            flow.flags |= flow.flags & FlowFlags.Referenced ? FlowFlags.Shared : FlowFlags.Referenced;
        }

        /** 添加前兆 */
        function addAntecedent(label: FlowLabel, antecedent: FlowNode): void {
            if (!(antecedent.flags & FlowFlags.Unreachable) && !contains(label.antecedents, antecedent)) {
                (label.antecedents || (label.antecedents = [])).push(antecedent);
                setFlowNodeReferenced(antecedent);
            }
        }

        function createFlowCondition(flags: FlowFlags, antecedent: FlowNode, expression: Expression | undefined): FlowNode {
            if (antecedent.flags & FlowFlags.Unreachable) {
                return antecedent;
            }
            if (!expression) {
                return flags & FlowFlags.TrueCondition ? antecedent : unreachableFlow;
            }
            if (expression.kind === SyntaxKind.TrueKeyword && flags & FlowFlags.FalseCondition ||
                expression.kind === SyntaxKind.FalseKeyword && flags & FlowFlags.TrueCondition) {
                if (!isExpressionOfOptionalChainRoot(expression)) {
                    return unreachableFlow;
                }
            }
            if (!isNarrowingExpression(expression)) {
                return antecedent;
            }
            setFlowNodeReferenced(antecedent);
            return flowNodeCreated({ flags, antecedent, node: expression });
        }

        function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode {
            if (!isNarrowingExpression(switchStatement.expression)) {
                return antecedent;
            }
            setFlowNodeReferenced(antecedent);
            return flowNodeCreated({ flags: FlowFlags.SwitchClause, antecedent, switchStatement, clauseStart, clauseEnd });
        }

        function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration): FlowNode {
            setFlowNodeReferenced(antecedent);
            return flowNodeCreated({ flags: FlowFlags.Assignment, antecedent, node });
        }

        function createFlowCall(antecedent: FlowNode, node: CallExpression ): FlowNode {
            setFlowNodeReferenced(antecedent);
            return flowNodeCreated({ flags: FlowFlags.Call, antecedent, node });
        }

        function createFlowArrayMutation(antecedent: FlowNode, node: CallExpression | BinaryExpression): FlowNode {
            setFlowNodeReferenced(antecedent);
            return flowNodeCreated({ flags: FlowFlags.ArrayMutation, antecedent, node });
        }

        function finishFlowLabel(flow: FlowLabel): FlowNode {
            const antecedents = flow.antecedents;
            if (!antecedents) {
                return unreachableFlow;
            }
            if (antecedents.length === 1) {
                return antecedents[0];
            }
            return flow;
        }

        function isStatementCondition(node: Node) {
            const parent = node.parent;
            switch (parent.kind) {
                case SyntaxKind.IfStatement:
                    return (<IfStatement>parent).expression === node;
                case SyntaxKind.ForStatement:
                case SyntaxKind.ConditionalExpression:
                    return (<ForStatement | ConditionalExpression>parent).condition === node;
            }
            return false;
        }

        function isLogicalExpression(node: Node) {
            while (true) {
                if (node.kind === SyntaxKind.ParenthesizedExpression) {
                    node = (<ParenthesizedExpression>node).expression;
                }
                else if (node.kind === SyntaxKind.PrefixUnaryExpression && (<PrefixUnaryExpression>node).operator === SyntaxKind.ExclamationToken) {
                    node = (<PrefixUnaryExpression>node).operand;
                }
                else {
                    return node.kind === SyntaxKind.BinaryExpression && (
                        (<BinaryExpression>node).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken ||
                        (<BinaryExpression>node).operatorToken.kind === SyntaxKind.BarBarToken);
                }
            }
        }

        function isTopLevelLogicalExpression(node: Node): boolean {
            while (isParenthesizedExpression(node.parent) ||
                isPrefixUnaryExpression(node.parent) && node.parent.operator === SyntaxKind.ExclamationToken) {
                node = node.parent;
            }
            return !isStatementCondition(node) &&
                !isLogicalExpression(node.parent) &&
                !(isOptionalChain(node.parent) && node.parent.expression === node);
        }

        function doWithConditionalBranches<T>(action: (value: T) => void, value: T, trueTarget: FlowLabel, falseTarget: FlowLabel) {
            const savedTrueTarget = currentTrueTarget;
            const savedFalseTarget = currentFalseTarget;
            currentTrueTarget = trueTarget;
            currentFalseTarget = falseTarget;
            action(value);
            currentTrueTarget = savedTrueTarget;
            currentFalseTarget = savedFalseTarget;
        }

        function bindCondition(node: Expression | undefined, trueTarget: FlowLabel, falseTarget: FlowLabel) {
            doWithConditionalBranches(bind, node, trueTarget, falseTarget);
            if (!node || !isLogicalExpression(node) && !(isOptionalChain(node) && isOutermostOptionalChain(node))) {
                addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
                addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
            }
        }

        function bindIterativeStatement(node: Statement, breakTarget: FlowLabel, continueTarget: FlowLabel): void {
            const saveBreakTarget = currentBreakTarget;
            const saveContinueTarget = currentContinueTarget;
            currentBreakTarget = breakTarget;
            currentContinueTarget = continueTarget;
            bind(node);
            currentBreakTarget = saveBreakTarget;
            currentContinueTarget = saveContinueTarget;
        }

        function bindWhileStatement(node: WhileStatement): void {
            const preWhileLabel = createLoopLabel();
            const preBodyLabel = createBranchLabel();
            const postWhileLabel = createBranchLabel();
            addAntecedent(preWhileLabel, currentFlow);
            currentFlow = preWhileLabel;
            bindCondition(node.expression, preBodyLabel, postWhileLabel);
            currentFlow = finishFlowLabel(preBodyLabel);
            bindIterativeStatement(node.statement, postWhileLabel, preWhileLabel);
            addAntecedent(preWhileLabel, currentFlow);
            currentFlow = finishFlowLabel(postWhileLabel);
        }

        function bindForStatement(node: ForStatement): void {
            const preLoopLabel = createLoopLabel();
            const preBodyLabel = createBranchLabel();
            const postLoopLabel = createBranchLabel();
            bind(node.initializer);
            addAntecedent(preLoopLabel, currentFlow);
            currentFlow = preLoopLabel;
            bindCondition(node.condition, preBodyLabel, postLoopLabel);
            currentFlow = finishFlowLabel(preBodyLabel);
            bindIterativeStatement(node.statement, postLoopLabel, preLoopLabel);
            bind(node.incrementor);
            addAntecedent(preLoopLabel, currentFlow);
            currentFlow = finishFlowLabel(postLoopLabel);
        }

        function bindForInOrForOfStatement(node: ForInOrOfStatement): void {
            const preLoopLabel = createLoopLabel();
            const postLoopLabel = createBranchLabel();
            bind(node.expression);
            addAntecedent(preLoopLabel, currentFlow);
            currentFlow = preLoopLabel;
            addAntecedent(postLoopLabel, currentFlow);
            bind(node.initializer);
            if (node.initializer.kind !== SyntaxKind.VariableDeclarationList) {
                bindAssignmentTargetFlow(node.initializer);
            }
            bindIterativeStatement(node.statement, postLoopLabel, preLoopLabel);
            addAntecedent(preLoopLabel, currentFlow);
            currentFlow = finishFlowLabel(postLoopLabel);
        }

        function bindIfStatement(node: IfStatement): void {
            const thenLabel = createBranchLabel();
            const elseLabel = createBranchLabel();
            const postIfLabel = createBranchLabel();
            bindCondition(node.expression, thenLabel, elseLabel);
            currentFlow = finishFlowLabel(thenLabel);
            bind(node.thenStatement);
            addAntecedent(postIfLabel, currentFlow);
            currentFlow = finishFlowLabel(elseLabel);
            bind(node.elseStatement);
            addAntecedent(postIfLabel, currentFlow);
            currentFlow = finishFlowLabel(postIfLabel);
        }

        function bindReturnOrThrow(node: ReturnStatement | ThrowStatement): void {
            bind(node.expression);
            if (node.kind === SyntaxKind.ReturnStatement) {
                hasExplicitReturn = true;
                if (currentReturnTarget) {
                    addAntecedent(currentReturnTarget, currentFlow);
                }
            }
            currentFlow = unreachableFlow;
        }

        function findActiveLabel(name: string) {
            if (activeLabels) {
                for (const label of activeLabels) {
                    if (label.name === name) {
                        return label;
                    }
                }
            }
            return undefined;
        }

        function bindBreakOrContinueFlow(node: BreakOrContinueStatement, breakTarget: FlowLabel | undefined, continueTarget: FlowLabel | undefined) {
            const flowLabel = node.kind === SyntaxKind.BreakStatement ? breakTarget : continueTarget;
            if (flowLabel) {
                addAntecedent(flowLabel, currentFlow);
                currentFlow = unreachableFlow;
            }
        }

        function bindBreakOrContinueStatement(node: BreakOrContinueStatement): void {
            bind(node.label);
            if (node.label) {
                const activeLabel = findActiveLabel(node.label.escapedText);
                if (activeLabel) {
                    activeLabel.referenced = true;
                    bindBreakOrContinueFlow(node, activeLabel.breakTarget, activeLabel.continueTarget);
                }
            }
            else {
                bindBreakOrContinueFlow(node, currentBreakTarget, currentContinueTarget);
            }
        }

        function bindTryStatement(node: TryStatement): void {
            const preFinallyLabel = createBranchLabel();
            const preTryFlow = currentFlow;
            const tryPriors: FlowNode[] = [];
            const oldFlowNodeCreated = flowNodeCreated;
            // We hook the creation of all flow nodes within the `try` scope and store them so we can add _all_ of them
            // as possible antecedents of the start of the `catch` or `finally` blocks.
            // Don't bother intercepting the call if there's no finally or catch block that needs the information
            if (node.catchClause || node.finallyBlock) {
                flowNodeCreated = node => (tryPriors.push(node), initFlowNode(node));
            }
            bind(node.tryBlock);
            flowNodeCreated = oldFlowNodeCreated;
            addAntecedent(preFinallyLabel, currentFlow);

            const flowAfterTry = currentFlow;
            let flowAfterCatch = unreachableFlow;

            if (node.catchClause) {
                currentFlow = preTryFlow;
                if (tryPriors.length) {
                    const preCatchFlow = createBranchLabel();
                    addAntecedent(preCatchFlow, currentFlow);
                    for (const p of tryPriors) {
                        addAntecedent(preCatchFlow, p);
                    }
                    currentFlow = finishFlowLabel(preCatchFlow);
                }

                bind(node.catchClause);
                addAntecedent(preFinallyLabel, currentFlow);

                flowAfterCatch = currentFlow;
            }
            if (node.finallyBlock) {
                // We add the nodes within the `try` block to the `finally`'s antecedents if there's no catch block
                // (If there is a `catch` block, it will have all these antecedents instead, and the `finally` will
                // have the end of the `try` block and the end of the `catch` block)
                let preFinallyPrior = preTryFlow;
                if (!node.catchClause) {
                    if (tryPriors.length) {
                        const preFinallyFlow = createBranchLabel();
                        addAntecedent(preFinallyFlow, preTryFlow);
                        for (const p of tryPriors) {
                            addAntecedent(preFinallyFlow, p);
                        }
                        preFinallyPrior = finishFlowLabel(preFinallyFlow);
                    }
                }

                // in finally flow is combined from pre-try/flow from try/flow from catch
                // pre-flow is necessary to make sure that finally is reachable even if finally flows in both try and finally blocks are unreachable

                // also for finally blocks we inject two extra edges into the flow graph.
                // first -> edge that connects pre-try flow with the label at the beginning of the finally block, it has lock associated with it
                // second -> edge that represents post-finally flow.
                // these edges are used in following scenario:
                // let a; (1)
                // try { a = someOperation(); (2)}
                // finally { (3) console.log(a) } (4)
                // (5) a

                // flow graph for this case looks roughly like this (arrows show ):
                // (1-pre-try-flow) <--.. <-- (2-post-try-flow)
                //  ^                                ^
                //  |*****(3-pre-finally-label) -----|
                //                ^
                //                |-- ... <-- (4-post-finally-label) <--- (5)
                // In case when we walk the flow starting from inside the finally block we want to take edge '*****' into account
                // since it ensures that finally is always reachable. However when we start outside the finally block and go through label (5)
                // then edge '*****' should be discarded because label 4 is only reachable if post-finally label-4 is reachable
                // Simply speaking code inside finally block is treated as reachable as pre-try-flow
                // since we conservatively assume that any line in try block can throw or return in which case we'll enter finally.
                // However code after finally is reachable only if control flow was not abrupted in try/catch or finally blocks - it should be composed from
                // final flows of these blocks without taking pre-try flow into account.
                //
                // extra edges that we inject allows to control this behavior
                // if when walking the flow we step on post-finally edge - we can mark matching pre-finally edge as locked so it will be skipped.
                const preFinallyFlow: PreFinallyFlow = initFlowNode({ flags: FlowFlags.PreFinally, antecedent: preFinallyPrior, lock: {} });
                addAntecedent(preFinallyLabel, preFinallyFlow);

                currentFlow = finishFlowLabel(preFinallyLabel);
                bind(node.finallyBlock);
                // if flow after finally is unreachable - keep it
                // otherwise check if flows after try and after catch are unreachable
                // if yes - convert current flow to unreachable
                // i.e.
                // try { return "1" } finally { console.log(1); }
                // console.log(2); // this line should be unreachable even if flow falls out of finally block
                if (!(currentFlow.flags & FlowFlags.Unreachable)) {
                    if ((flowAfterTry.flags & FlowFlags.Unreachable) && (flowAfterCatch.flags & FlowFlags.Unreachable)) {
                        currentFlow = flowAfterTry === reportedUnreachableFlow || flowAfterCatch === reportedUnreachableFlow
                            ? reportedUnreachableFlow
                            : unreachableFlow;
                    }
                }
                if (!(currentFlow.flags & FlowFlags.Unreachable)) {
                    const afterFinallyFlow: AfterFinallyFlow = flowNodeCreated({ flags: FlowFlags.AfterFinally, antecedent: currentFlow });
                    preFinallyFlow.lock = afterFinallyFlow;
                    currentFlow = afterFinallyFlow;
                }
            }
            else {
                currentFlow = finishFlowLabel(preFinallyLabel);
            }
        }


        function bindSwitchStatement(node: SwitchStatement): void {
            const postSwitchLabel = createBranchLabel();
            bind(node.expression);
            const saveBreakTarget = currentBreakTarget;
            const savePreSwitchCaseFlow = preSwitchCaseFlow;
            currentBreakTarget = postSwitchLabel;
            preSwitchCaseFlow = currentFlow;
            bind(node.caseBlock);
            addAntecedent(postSwitchLabel, currentFlow);
            const hasDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause);
            // We mark a switch statement as possibly exhaustive if it has no default clause and if all
            // case clauses have unreachable end points (e.g. they all return). Note, we no longer need
            // this property in control flow analysis, it's there only for backwards compatibility.
            node.possiblyExhaustive = !hasDefault && !postSwitchLabel.antecedents;
            if (!hasDefault) {
                addAntecedent(postSwitchLabel, createFlowSwitchClause(preSwitchCaseFlow, node, 0, 0));
            }
            currentBreakTarget = saveBreakTarget;
            preSwitchCaseFlow = savePreSwitchCaseFlow;
            currentFlow = finishFlowLabel(postSwitchLabel);
        }

        function bindCaseBlock(node: CaseBlock): void {
            const clauses = node.clauses;
            for (let i = 0; i < clauses.length; i++) {
                const clauseStart = i;
                while (!clauses[i].statements.length && i + 1 < clauses.length) {
                    bind(clauses[i]);
                    i++;
                }
                const preCaseLabel = createBranchLabel();
                addAntecedent(preCaseLabel, createFlowSwitchClause(preSwitchCaseFlow!, node.parent, clauseStart, i + 1));
                addAntecedent(preCaseLabel, unreachableFlow);
                currentFlow = finishFlowLabel(preCaseLabel);
                const clause = clauses[i];
                bind(clause);
                if (!(currentFlow.flags & FlowFlags.Unreachable) && i !== clauses.length - 1) {
                    clause.fallthroughFlowNode = currentFlow;
                }
            }
        }

        function bindCaseClause(node: CaseClause): void {
            const saveCurrentFlow = currentFlow;
            currentFlow = preSwitchCaseFlow!;
            bind(node.expression);
            currentFlow = saveCurrentFlow;

            bindEach(node.statements);
        }

        function pushActiveLabel(name: string, breakTarget: FlowLabel, continueTarget: FlowLabel, node: LabeledStatement): ActiveLabel {
            const activeLabel: ActiveLabel = {
                node,
                name,
                breakTarget,
                continueTarget,
                referenced: false
            };
            (activeLabels || (activeLabels = [])).push(activeLabel);
            return activeLabel;
        }

        function popActiveLabel() {
            activeLabels!.pop();
        }

        function bindExpressionStatement(node: ExpressionStatement): void {
            bind(node.expression);
            // A top level call expression with a dotted function name and at least one argument
            // is potentially an assertion and is therefore included in the control flow.
            if (node.expression.kind === SyntaxKind.CallExpression) {
                const call = <CallExpression>node.expression;
                if (isDottedName(call.expression)) {
                    currentFlow = createFlowCall(currentFlow, call);
                }
            }
        }

        function bindLabeledStatement(node: LabeledStatement): void {
            const preStatementLabel = createLoopLabel();
            const postStatementLabel = createBranchLabel();
            bind(node.label);
            addAntecedent(preStatementLabel, currentFlow);
            const activeLabel = pushActiveLabel(node.label.escapedText, postStatementLabel, preStatementLabel, node);
            bind(node.statement);
            popActiveLabel();
            if (!activeLabel.referenced) {
                errorOrSuggestionOnNode(unusedLabelIsError(), node.label, Diagnostics.Unused_label);
            }
            if (!node.statement) {
                // do statement sets current flow inside bindDoStatement
                addAntecedent(postStatementLabel, currentFlow);
                currentFlow = finishFlowLabel(postStatementLabel);
            }
        }

        function bindDestructuringTargetFlow(node: Expression) {
            if (node.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>node).operatorToken.kind === SyntaxKind.EqualsToken) {
                bindAssignmentTargetFlow((<BinaryExpression>node).left);
            }
            else {
                bindAssignmentTargetFlow(node);
            }
        }

        function bindAssignmentTargetFlow(node: Expression) {
            if (isNarrowableReference(node)) {
                currentFlow = createFlowAssignment(currentFlow, node);
            }
            else if (node.kind === SyntaxKind.ArrayLiteralExpression) {
                for (const e of (<ArrayLiteralExpression>node).elements) {
                    if (e.kind === SyntaxKind.SpreadElement) {
                        bindAssignmentTargetFlow((<SpreadElement>e).expression);
                    }
                    else {
                        bindDestructuringTargetFlow(e);
                    }
                }
            }
            else if (node.kind === SyntaxKind.ObjectLiteralExpression) {
                for (const p of (<ObjectLiteralExpression>node).properties) {
                    if (p.kind === SyntaxKind.PropertyAssignment) {
                        bindDestructuringTargetFlow(p.initializer);
                    }
                    else if (p.kind === SyntaxKind.ShorthandPropertyAssignment) {
                        bindAssignmentTargetFlow(p.name);
                    }
                }
            }
        }

        function bindLogicalExpression(node: BinaryExpression, trueTarget: FlowLabel, falseTarget: FlowLabel) {
            const preRightLabel = createBranchLabel();
            if (node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) {
                bindCondition(node.left, preRightLabel, falseTarget);
            }
            else {
                bindCondition(node.left, trueTarget, preRightLabel);
            }
            currentFlow = finishFlowLabel(preRightLabel);
            bind(node.operatorToken);
            bindCondition(node.right, trueTarget, falseTarget);
        }

        function bindPrefixUnaryExpressionFlow(node: PrefixUnaryExpression) {
            if (node.operator === SyntaxKind.ExclamationToken) {
                const saveTrueTarget = currentTrueTarget;
                currentTrueTarget = currentFalseTarget;
                currentFalseTarget = saveTrueTarget;
                bindEachChild(node);
                currentFalseTarget = currentTrueTarget;
                currentTrueTarget = saveTrueTarget;
            }
            else {
                bindEachChild(node);
                if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) {
                    bindAssignmentTargetFlow(node.operand);
                }
            }
        }

        function bindPostfixUnaryExpressionFlow(node: PostfixUnaryExpression) {
            bindEachChild(node);
            if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) {
                bindAssignmentTargetFlow(node.operand);
            }
        }

        function bindBinaryExpressionFlow(node: BinaryExpression) {
            const operator = node.operatorToken.kind;
            if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) {
                if (isTopLevelLogicalExpression(node)) {
                    const postExpressionLabel = createBranchLabel();
                    bindLogicalExpression(node, postExpressionLabel, postExpressionLabel);
                    currentFlow = finishFlowLabel(postExpressionLabel);
                }
                else {
                    bindLogicalExpression(node, currentTrueTarget!, currentFalseTarget!);
                }
            }
            else {
                bindEachChild(node);
                if (isAssignmentOperator(operator) && !isAssignmentTarget(node)) {
                    bindAssignmentTargetFlow(node.left);
                    if (operator === SyntaxKind.EqualsToken && node.left.kind === SyntaxKind.ElementAccessExpression) {
                        const elementAccess = <ElementAccessExpression>node.left;
                        if (isNarrowableOperand(elementAccess.expression)) {
                            currentFlow = createFlowArrayMutation(currentFlow, node);
                        }
                    }
                }
            }
        }

        function bindConditionalExpressionFlow(node: ConditionalExpression) {
            const trueLabel = createBranchLabel();
            const falseLabel = createBranchLabel();
            const postExpressionLabel = createBranchLabel();
            bindCondition(node.condition, trueLabel, falseLabel);
            currentFlow = finishFlowLabel(trueLabel);
            bind(node.questionToken);
            bind(node.whenTrue);
            addAntecedent(postExpressionLabel, currentFlow);
            currentFlow = finishFlowLabel(falseLabel);
            bind(node.colonToken);
            bind(node.whenFalse);
            addAntecedent(postExpressionLabel, currentFlow);
            currentFlow = finishFlowLabel(postExpressionLabel);
        }

        function bindInitializedVariableFlow(node: VariableDeclaration | ArrayBindingElement) {
            currentFlow = createFlowAssignment(currentFlow, node);
        }

        function bindVariableDeclarationFlow(node: VariableDeclaration) {
            bindEachChild(node);
            if (node.initializer || isForInOrOfStatement(node.parent.parent)) {
                bindInitializedVariableFlow(node);
            }
        }

        function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag) {
            node.tagName.parent = node;
            if (node.kind !== SyntaxKind.JSDocEnumTag && node.fullName) {
                setParentPointers(node, node.fullName);
            }
        }

        function isOutermostOptionalChain(node: OptionalChain) {
            return !isOptionalChain(node.parent) || isOptionalChainRoot(node.parent) || node !== node.parent.expression;
        }

        function bindOptionalExpression(node: Expression, trueTarget: FlowLabel, falseTarget: FlowLabel) {
            doWithConditionalBranches(bind, node, trueTarget, falseTarget);
            if (!isOptionalChain(node) || isOutermostOptionalChain(node)) {
                addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
                addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
            }
        }

        function bindOptionalChainRest(node: OptionalChain) {
            bind(node.questionDotToken);
            switch (node.kind) {
                case SyntaxKind.PropertyAccessExpression:
                    bind(node.name);
                    break;
                case SyntaxKind.ElementAccessExpression:
                    bind(node.argumentExpression);
                    break;
                case SyntaxKind.CallExpression:
                    bindEach(node.typeArguments);
                    bindEach(node.arguments);
                    break;
            }
        }

        function bindOptionalChain(node: OptionalChain, trueTarget: FlowLabel, falseTarget: FlowLabel) {
            // For an optional chain, we emulate the behavior of a logical expression:
            //
            // a?.b         -> a && a.b
            // a?.b.c       -> a && a.b.c
            // a?.b?.c      -> a && a.b && a.b.c
            // a?.[x = 1]   -> a && a[x = 1]
            //
            // To do this we descend through the chain until we reach the root of a chain (the expression with a `?.`)
            // and build it's CFA graph as if it were the first condition (`a && ...`). Then we bind the rest
            // of the node as part of the "true" branch, and continue to do so as we ascend back up to the outermost
            // chain node. We then treat the entire node as the right side of the expression.
            const preChainLabel = node.questionDotToken ? createBranchLabel() : undefined;
            bindOptionalExpression(node.expression, preChainLabel || trueTarget, falseTarget);
            if (preChainLabel) {
                currentFlow = finishFlowLabel(preChainLabel);
            }
            doWithConditionalBranches(bindOptionalChainRest, node, trueTarget, falseTarget);
            if (isOutermostOptionalChain(node)) {
                addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node));
                addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node));
            }
        }

        function bindOptionalChainFlow(node: OptionalChain) {
            if (isTopLevelLogicalExpression(node)) {
                const postExpressionLabel = createBranchLabel();
                bindOptionalChain(node, postExpressionLabel, postExpressionLabel);
                currentFlow = finishFlowLabel(postExpressionLabel);
            }
            else {
                bindOptionalChain(node, currentTrueTarget!, currentFalseTarget!);
            }
        }

        function bindAccessExpressionFlow(node: AccessExpression) {
            if (isOptionalChain(node)) {
                bindOptionalChainFlow(node);
            }
            else {
                bindEachChild(node);
            }
        }

        function bindCallExpressionFlow(node: CallExpression) {
            if (isOptionalChain(node)) {
                bindOptionalChainFlow(node);
            }
            else {
                // If the target of the call expression is a function expression or arrow function we have
                // an immediately invoked function expression (IIFE). Initialize the flowNode property to
                // the current control flow (which includes evaluation of the IIFE arguments).
                const expr = skipParentheses(node.expression);
                if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) {
                    bindEach(node.typeArguments);
                    bindEach(node.arguments);
                    bind(node.expression);
                }
                else {
                    bindEachChild(node);
                }
            }
        }

        function addToContainerChain(next: Node) {
            if (lastContainer) {
                lastContainer.nextContainer = next;
            }

            lastContainer = next;
        }

        function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol | undefined {
            switch (container.kind) {

                case SyntaxKind.SourceFile:
                    return declareSourceFileMember(node, symbolFlags, symbolExcludes);

                case SyntaxKind.EnumDeclaration:
                    return declareSymbol(container.symbol.members!, container.symbol, node, symbolFlags, symbolExcludes);

                case SyntaxKind.TypeLiteral:
                case SyntaxKind.JSDocTypeLiteral:
                case SyntaxKind.ObjectLiteralExpression:
                case SyntaxKind.InterfaceDeclaration:
                case SyntaxKind.程序声明_:
                case SyntaxKind.对象声明_:
                case SyntaxKind.结构声明_:
                case SyntaxKind.类型声明_:
                    // Interface/Object-types always have their children added to the 'members' of
                    // their container. They are only accessible through an instance of their
                    // container, and are never in scope otherwise (even inside the body of the
                    // object / type / interface declaring them). An exception is type parameters,
                    // which are in scope without qualification (similar to 'locals').
                    return declareSymbol(container.symbol.members!, container.symbol, node, symbolFlags, symbolExcludes);

                case SyntaxKind.FunctionType:
                case SyntaxKind.JSDocSignature:
                case SyntaxKind.内部方法声明_:
                case SyntaxKind.MethodDeclaration:
                case SyntaxKind.MethodSignature:
                case SyntaxKind.ConstructSignature:
                case SyntaxKind.ConstructDeclaration:
                case SyntaxKind.释放声明_:
                case SyntaxKind.FunctionDeclaration:
                case SyntaxKind.FunctionExpression:
                case SyntaxKind.ArrowFunction:
                case SyntaxKind.JSDocFunctionType:
                case SyntaxKind.JSDocTypedefTag:
                case SyntaxKind.JSDocCallbackTag:
                case SyntaxKind.别名声明_:
                case SyntaxKind.测试单元语句_:
                case SyntaxKind.同步语句_:
                    // All the children of these container types are never visible through another
                    // symbol (i.e. through another symbol's 'exports' or 'members').  Instead,
                    // they're only accessed 'lexically' (i.e. from code that exists underneath
                    // their container in the tree). To accomplish this, we simply add their declared
                    // symbol to the 'locals' of the container.  These symbols can then be found as
                    // the type checker walks up the containers, checking them for matching names.
                    return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
                case SyntaxKind.Block:
                    if ((<Block>container).版本容器块种类) {
                        return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
                    }
            }
        }

        function declareSourceFileMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
            return declareSymbol(file.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
        }

        function bindFunctionOrConstructorType(node: SignatureDeclaration | JSDocSignature): void {
            // For a given function symbol "<...>(...) => T" we want to generate a symbol identical
            // to the one we would get for: { <...>(...): T }
            //
            // We do that by making an anonymous type literal symbol, and then setting the function
            // symbol as its sole member. To the rest of the system, this symbol will be indistinguishable
            // from an actual type literal symbol you would have gotten had you used the long form.
            const name = getDeclarationName(node)!;
            const symbol = createSymbol(SymbolFlags.Signature, name, undefined); // TODO: GH#18217
            addDeclarationToSymbol(symbol, node, SymbolFlags.Signature);

            const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type, undefined);
            addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral);
            typeLiteralSymbol.members = createSymbolTable();
            typeLiteralSymbol.members.set(symbol.escapedName, symbol);
        }

        function bindObjectLiteralExpression(node: ObjectLiteralExpression) {
            return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, InternalSymbolName.Object);
        }

        function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string) {
            const symbol = createSymbol(symbolFlags, name, undefined);
            if (symbolFlags & SymbolFlags.EnumMember) {
                symbol.parent = container.symbol;
            }
            addDeclarationToSymbol(symbol, node, symbolFlags);
            return symbol;
        }

        function bindBlockScopedDeclaration(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
            switch (blockScopeContainer.kind) {
                default:
                    if (!blockScopeContainer.locals) {
                        blockScopeContainer.locals = createSymbolTable();
                        addToContainerChain(blockScopeContainer);
                    }
                    declareSymbol(blockScopeContainer.locals, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
            }
        }

        function delayedBindJSDocTypedefTag() {
            if (!delayedTypeAliases) {
                return;
            }
            const saveContainer = container;
            const saveLastContainer = lastContainer;
            const saveBlockScopeContainer = blockScopeContainer;
            const saveParent = parent;
            const saveCurrentFlow = currentFlow;
            for (const typeAlias of delayedTypeAliases) {
                const host = getJSDocHost(typeAlias);
                container = findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) || file;
                blockScopeContainer = getEnclosingBlockScopeContainer(host) || file;
                currentFlow = initFlowNode({ flags: FlowFlags.Start });
                parent = typeAlias;
                bind(typeAlias.typeExpression);
                if (isJSDocEnumTag(typeAlias) || !typeAlias.fullName || typeAlias.fullName.kind === SyntaxKind.Identifier) {
                    parent = typeAlias.parent;
                    bindBlockScopedDeclaration(typeAlias, SymbolFlags.类型别名_, SymbolFlags.TypeAliasExcludes);
                }
                else {
                    bind(typeAlias.fullName);
                }
            }
            container = saveContainer;
            lastContainer = saveLastContainer;
            blockScopeContainer = saveBlockScopeContainer;
            parent = saveParent;
            currentFlow = saveCurrentFlow;
        }

        function checkStrictModeLabeledStatement(node: LabeledStatement) {
            if (isDeclarationStatement(node.statement) || isVariableStatement(node.statement)) {
                errorOnFirstToken(node.label, Diagnostics.A_label_is_not_allowed_here);
            }
        }

        function error(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any) {
            file.bindDiagnostics.push(createFileDiagnostic(file, node.pos, node.end- node.pos, message, arg0, arg1, arg2));
        }

        function errorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any) {
            const span = getSpanOfTokenAtPosition(file, node.pos);
            file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, arg0, arg1, arg2));
        }

        function errorOrSuggestionOnNode(isError: boolean, node: Node, message: DiagnosticMessage): void {
            errorOrSuggestionOnRange(isError, node, node, message);
        }

        function errorOrSuggestionOnRange(isError: boolean, startNode: Node, endNode: Node, message: DiagnosticMessage): void {
            addErrorOrSuggestionDiagnostic(isError, { pos: getTokenPosOfNode(startNode, file), end: endNode.end }, message);
        }

        function addErrorOrSuggestionDiagnostic(isError: boolean, range: TextRange, message: DiagnosticMessage): void {
            const diag = createFileDiagnostic(file, range.pos, range.end - range.pos, message);
            if (isError) {
                file.bindDiagnostics.push(diag);
            }
            else {
                file.bindSuggestionDiagnostics = append(file.bindSuggestionDiagnostics, { ...diag, category: DiagnosticCategory.Suggestion });
            }
        }

        function bind(node: Node | undefined): void {
            if (!node) {
                return;
            }
            node.parent = parent;
            const saveInStrictMode = inStrictMode;

            // Even though in the AST the jsdoc @typedef node belongs to the current node,
            // its symbol might be in the same scope with the current node's symbol. Consider:
            //
            //     /** @typedef {string | number} MyType */
            //     function foo();
            //
            // Here the current node is "foo", which is a container, but the scope of "MyType" should
            // not be inside "foo". Therefore we always bind @typedef before bind the parent node,
            // and skip binding this tag later when binding all the other jsdoc tags.

            // First we bind declaration nodes to a symbol if possible. We'll both create a symbol
            // and then potentially add the symbol to an appropriate symbol table. Possible
            // destination symbol tables are:
            //
            //  1) The 'exports' table of the current container's symbol.
            //  2) The 'members' table of the current container's symbol.
            //  3) The 'locals' table of the current container.
            //
            // However, not all symbols will end up in any of these tables. 'Anonymous' symbols
            // (like TypeLiterals for example) will not be put in any table.
            bindWorker(node);
            // Then we recurse into the children of the node to bind them as well. For certain
            // symbols we do specialized work when we recurse. For example, we'll keep track of
            // the current 'container' node when it changes. This helps us know which symbol table
            // a local should go into for example. Since terminal nodes are known not to have
            // children, as an optimization we don't process those.
            if (node.kind > SyntaxKind.LastToken) {
                const saveParent = parent;
                const 储存的版本路径 = 当前版本路径;
                parent = node;
                const containerFlags = getContainerFlags(node);
                if (containerFlags === ContainerFlags.None) {
                    bindChildren(node);
                }
                else {
                    bindContainer(node, containerFlags);
                }
                当前版本路径 = 储存的版本路径;
                parent = saveParent;
            }
            else{
                const saveParent = parent;
                if (node.kind === SyntaxKind.EndOfFileToken) parent = node;
                bindJSDoc(node);
                parent = saveParent;
            }
            inStrictMode = saveInStrictMode;
        }

        function bindJSDoc(node: Node) {
            if (hasJSDocNodes(node)) {
                for (const j of node.jsDoc!) {
                    setParentPointers(node, j);
                }
            }
            if (有转换词典节点(node)) {
                for (const d of node.dict!) {
                    setParentPointers(node, d);
                }
            }
        }
    
        function bindWorker(node: Node) {
            switch (node.kind) {
                case SyntaxKind.程序声明_:
                    return declareSymbolAndAddToSymbolTable(node as 程序声明_, SymbolFlags.程序_, SymbolFlags.程序排除_);
                case SyntaxKind.导入从成员_:
                    return declareSymbolAndAddToSymbolTable(node as 导入从成员_, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
                case SyntaxKind.导入成员_:
                    return declareSymbolAndAddToSymbolTable(node as 导入成员_, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
                case SyntaxKind.Identifier:
                case SyntaxKind.ThisKeyword:
                case SyntaxKind.原型关键字_:
                    // for typedef type names with namespaces, bind the new jsdoc type symbol here
                    // because it requires all containing namespaces to be in effect, namely the
                    // current "blockScopeContainer" needs to be set to its immediate namespace parent.
                    if ((<Identifier>node).isInJSDocNamespace) {
                        let parentNode = node.parent;
                        while (parentNode && !isJSDocTypeAlias(parentNode)) {
                            parentNode = parentNode.parent;
                        }
                        bindBlockScopedDeclaration(parentNode as Declaration, SymbolFlags.类型别名_, SymbolFlags.TypeAliasExcludes);
                    }
                    if (currentFlow && (isExpression(node) || parent.kind === SyntaxKind.TypeAssertionExpression || parent.kind === SyntaxKind.ShorthandPropertyAssignment)) {
                        node.flowNode = currentFlow;
                    }
                    break;
                case SyntaxKind.PropertyAccessExpression:
                case SyntaxKind.ElementAccessExpression:
                    const expr = node as PropertyAccessExpression | ElementAccessExpression;
                    if (currentFlow && isNarrowableReference(expr)) {
                        expr.flowNode = currentFlow;
                    }
                    break;
                case SyntaxKind.LabeledStatement:
                    return checkStrictModeLabeledStatement(<LabeledStatement>node);
                case SyntaxKind.TypeParameter:
                    return bindTypeParameter(node as TypeParameterDeclaration);
                case SyntaxKind.Parameter:
                    return bindParameter(<ParameterDeclaration>node);
                case SyntaxKind.VariableDeclaration:
                    return bindVariableDeclarationOrBindingElement(<VariableDeclaration>node);
                case SyntaxKind.PropertyDeclaration:
                case SyntaxKind.PropertySignature:
                case SyntaxKind.释放声明_:
                    return bindPropertyWorker(node as PropertyDeclaration | PropertySignature);
                case SyntaxKind.PropertyAssignment:
                    if (node.parent.kind === SyntaxKind.NewExpression) {
                        break;
                    }
                case SyntaxKind.ShorthandPropertyAssignment:
                    return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
                case SyntaxKind.EnumMember:
                    return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes);
                case SyntaxKind.内部方法声明_:
                    return 绑定内部方法声明(<内部方法声明_>node);
                case SyntaxKind.MethodDeclaration:
                    return 绑定方法声明(<MethodDeclaration>node);
                case SyntaxKind.ConstructSignature:
                case SyntaxKind.ConstructDeclaration:
                    return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.Signature, SymbolFlags.签名排除_);
                case SyntaxKind.MethodSignature:
                    return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.方法签名_ | SymbolFlags.Method | ((<MethodDeclaration>node).questionToken ? SymbolFlags.Optional : SymbolFlags.None),
                        isObjectLiteralMethod(node) ? SymbolFlags.PropertyExcludes : SymbolFlags.MethodExcludes);
                case SyntaxKind.FunctionDeclaration:
                    return bindFunctionDeclaration(<FunctionDeclaration>node);
                case SyntaxKind.FunctionType:
                case SyntaxKind.JSDocFunctionType:
                case SyntaxKind.JSDocSignature:
                    return bindFunctionOrConstructorType(<SignatureDeclaration | JSDocSignature>node);
                case SyntaxKind.TypeLiteral:
                case SyntaxKind.JSDocTypeLiteral:
                    return bindAnonymousTypeWorker(node as TypeLiteralNode | JSDocTypeLiteral);
                case SyntaxKind.ObjectLiteralExpression:
                    return bindObjectLiteralExpression(<ObjectLiteralExpression>node);
                case SyntaxKind.FunctionExpression:
                case SyntaxKind.ArrowFunction:
                    return bindFunctionExpression(<FunctionExpression>node);
                case SyntaxKind.CallExpression:
                    const assignmentKind = getAssignmentDeclarationKind(node as CallExpression);
                    switch (assignmentKind) {
                        case AssignmentDeclarationKind.None:
                            break; // Nothing to do
                        default:
                            return Debug.fail("Unknown call expression assignment declaration kind");
                    }
                    break;
                case SyntaxKind.结构声明_:
                    外部容器节点 = node;
                    return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.结构_, SymbolFlags.对象排除_);
                case SyntaxKind.对象声明_:
                    外部容器节点 = node;
                    return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.对象_, SymbolFlags.对象排除_);
                case SyntaxKind.InterfaceDeclaration:
                    return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes);
                case SyntaxKind.类型声明_:
                    return 绑定类型声明(<类型声明节点_>node);
                case SyntaxKind.别名声明_:
                    return declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.类型别名_, SymbolFlags.TypeAliasExcludes);
                case SyntaxKind.EnumDeclaration:
                    return bindEnumDeclaration(<EnumDeclaration>node);
                case SyntaxKind.版本表达式语句_:
                    return 声明版本符号并添加到符号表(<版本表达式语句_>node);
                case SyntaxKind.测试单元语句_:
                case SyntaxKind.导入从成员块_:
                case SyntaxKind.导入成员块_:
                case SyntaxKind.Block:
                    if ((<Block>node).版本容器块种类) {
                        return 计算当前版本路径(<Block | 导入从成员块_ | 测试单元语句_ | 导入成员块_>node);
                    }
                    break;
                case SyntaxKind.JSDocParameterTag:
                    if (node.parent.kind === SyntaxKind.JSDocSignature) {
                        return bindParameter(node as JSDocParameterTag);
                    }
                    if (node.parent.kind !== SyntaxKind.JSDocTypeLiteral) {
                        break;
                    }
                // falls through
                case SyntaxKind.JSDocPropertyTag:
                    const propTag = node as JSDocPropertyLikeTag;
                    const flags = propTag.isBracketed || propTag.typeExpression && propTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType ?
                        SymbolFlags.Property | SymbolFlags.Optional :
                        SymbolFlags.Property;
                    return declareSymbolAndAddToSymbolTable(propTag, flags, SymbolFlags.PropertyExcludes);
                case SyntaxKind.JSDocTypedefTag:
                case SyntaxKind.JSDocCallbackTag:
                case SyntaxKind.JSDocEnumTag:
                    return (delayedTypeAliases || (delayedTypeAliases = [])).push(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag);
            }
        }

        function 绑定类型声明(node: 类型声明节点_) {
            const 类型声明 = declareSymbolAndAddToSymbolTable(<Declaration>node, SymbolFlags.类型声明_, SymbolFlags.类型声明排除_);
            const 原型符号 = createSymbol(SymbolFlags.Property | SymbolFlags.原型_, InternalSymbolName.原型_, undefined);
            类型声明?.members!.set(原型符号.escapedName, 原型符号);
            原型符号.parent = 类型声明;
        }

        function 绑定内部方法声明(node: 内部方法声明_) {
            container.symbol.有方法 = true;
            return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Method, SymbolFlags.MethodExcludes);
        }

        function 绑定方法声明(node: MethodDeclaration) {
            const name = node.name.escapedText;
            const 转换词典 = 遍历祖先词典表达式(node, name);
            if (转换词典) {
                const 名称 = (<Identifier>转换词典.值).escapedText || InternalSymbolName.Missing;
                const dictSymbol = createSymbol(SymbolFlags.转换词典值_, 名称, undefined);
                dictSymbol.declarations = [转换词典];
                dictSymbol.valueDeclaration = 转换词典;
            }
            const symbol = createSymbol(SymbolFlags.Method | SymbolFlags.方法声明_, name, 转换词典);
            addDeclarationToSymbol(symbol, node, SymbolFlags.Method | SymbolFlags.方法声明_);
            外部容器节点 = node;
            return symbol;
        }

        function bindPropertyWorker(node: PropertyDeclaration | PropertySignature) {
            return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
        }

        function bindAnonymousTypeWorker(node: TypeLiteralNode | JSDocTypeLiteral) {
            return bindAnonymousDeclaration(<Declaration>node, SymbolFlags.TypeLiteral, InternalSymbolName.Type);
        }
    
        function bindEnumDeclaration(node: EnumDeclaration) {
            return declareSymbolAndAddToSymbolTable(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes)
        }

        function 声明版本符号并添加到符号表(node: 版本表达式语句_) {
            return declareSymbolAndAddToSymbolTable(node, SymbolFlags.版本声明_, SymbolFlags.排除版本_);
        }

        function bindVariableDeclarationOrBindingElement(node: VariableDeclaration) {
            if (isBlockOrCatchScoped(node)) {
                bindBlockScopedDeclaration(node, SymbolFlags.BlockScopedVariable, SymbolFlags.BlockScopedVariableExcludes);
                if (!(node.name.是私有的)) {
                    if (container.kind === SyntaxKind.程序声明_) {
                        declareSymbol(container.symbol.members!, container.symbol, node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes);
                    }
                }
            }
            else if (isParameterDeclaration(node)) {
                // It is safe to walk up parent chain to find whether the node is a destructuring parameter declaration
                // because its parent chain has already been set up, since parents are set before descending into children.
                //
                // If node is a binding element in parameter declaration, we need to use ParameterExcludes.
                // Using ParameterExcludes flag allows the compiler to report an error on duplicate identifiers in Parameter Declaration
                // For example:
                //      function foo([a,a]) {} // Duplicate Identifier error
                //      function bar(a,a) {}   // Duplicate Identifier error, parameter declaration in this case is handled in bindParameter
                //                             // which correctly set excluded symbols
                declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes);
            }
            else {
                declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes);
            }
        }

        function bindParameter(node: ParameterDeclaration | JSDocParameterTag) {
            if (node.kind === SyntaxKind.JSDocParameterTag && container.kind !== SyntaxKind.JSDocSignature) {
                return;
            }
            declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable | SymbolFlags.参数_, SymbolFlags.ParameterExcludes);
        }

        function bindFunctionDeclaration(node: FunctionDeclaration) {
            declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes);            
            外部容器节点 = node;
        }

        function bindFunctionExpression(node: FunctionExpression) {
            if (currentFlow) {
                node.flowNode = currentFlow;
            }
            const bindingName = node.name ? node.name.escapedText : InternalSymbolName.Function;
            return bindAnonymousDeclaration(node, SymbolFlags.Function, bindingName);
        }

        function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
            return declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
        }

        function bindTypeParameter(node: TypeParameterDeclaration) {
            if (isJSDocTemplateTag(node.parent)) {
                const container = find((node.parent.parent as JSDoc).tags!, isJSDocTypeAlias) || getHostSignatureFromJSDoc(node.parent); // TODO: GH#18217
                if (container) {
                    if (!container.locals) {
                        container.locals = createSymbolTable();
                    }
                    declareSymbol(container.locals, /*parent*/ undefined, node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
                }
                else {
                    declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
                }
            }
            else {
                declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
            }
        }

        // reachability checks

        function checkUnreachable(node: Node): boolean {
            if (!(currentFlow.flags & FlowFlags.Unreachable)) {
                return false;
            }
            if (currentFlow === unreachableFlow) {
                const reportError = isStatementButNotDeclaration(node) && node.kind !== SyntaxKind.EmptyStatement ||
                node.kind === SyntaxKind.对象声明_ ||
                node.kind === SyntaxKind.结构声明_;

                if (reportError) {
                    currentFlow = reportedUnreachableFlow;
                    // unreachable code is reported if
                    // - user has explicitly asked about it AND
                    // - statement is in not ambient context (statements in ambient context is already an error
                    //   so we should not report extras) AND
                    //   - node is not variable statement OR
                    //   - node is block scoped variable statement OR
                    //   - node is not block scoped variable statement and at least one variable declaration has initializer
                    //   Rationale: we don't want to report errors on non-initialized var's since they are hoisted
                    //   On the other side we do want to report errors on non-initialized 'lets' because of TDZ
                    const isError =
                        unreachableCodeIsError() &&
                        !(node.flags & NodeFlags.Ambient) &&
                        (
                            !isVariableStatement(node) ||
                            !!(getCombinedNodeFlags(node.declarationList) & NodeFlags.BlockScoped) ||
                            node.declarationList.declarations.some(d => !!d.initializer)
                        );

                    eachUnreachableRange(node, (start, end) => errorOrSuggestionOnRange(isError, start, end, Diagnostics.Unreachable_code_detected));

                }
            }
            return true;
        }
    }

    function eachUnreachableRange(node: Node, cb: (start: Node, last: Node) => void): void {
        if (isStatement(node) && isExecutableStatement(node) && isBlock(node.parent)) {
            const { statements } = node.parent;
            const slice = sliceAfter(statements, node);
            getRangesWhere(slice, isExecutableStatement, (start, afterEnd) => cb(slice[start], slice[afterEnd - 1]));
        }
        else {
            cb(node, node);
        }
    }
    // As opposed to a pure declaration like an `interface`
    function isExecutableStatement(s: Statement): boolean {
        // Don't remove statements that can validly be used before they appear.
        return !isFunctionDeclaration(s) && !isPurelyTypeDeclaration(s) && !isEnumDeclaration(s) &&
            // `var x;` may declare a variable used above
            !(isVariableStatement(s) && !(getCombinedNodeFlags(s) & (NodeFlags.Let | NodeFlags.Const)) && s.declarationList.declarations.some(d => !d.initializer));
    }

    function isPurelyTypeDeclaration(s: Statement): boolean {
        switch (s.kind) {
            case SyntaxKind.InterfaceDeclaration:
            case SyntaxKind.对象声明_:
            case SyntaxKind.结构声明_:
            case SyntaxKind.类型声明_:
            case SyntaxKind.别名声明_:
            case SyntaxKind.EnumDeclaration:
                return true;
            default:
                return false;
        }
    }

    export function isExportsOrModuleExportsOrAlias(sourceFile: SourceFile, node: Expression): boolean {
        let i = 0;
        const q = [node];
        while (q.length && i < 100) {
            i++;
            node = q.shift()!;
            if (isIdentifier(node)) {
                const symbol = lookupSymbolForNameWorker(sourceFile, node.escapedText);
                if (!!symbol && !!symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) && !!symbol.valueDeclaration.initializer) {
                    const init = symbol.valueDeclaration.initializer;
                    q.push(init);
                    if (isAssignmentExpression(init, /*excludeCompoundAssignment*/ true)) {
                        q.push(init.left);
                        q.push(init.right);
                    }
                }
            }
        }
        return false;
    }

    function lookupSymbolForNameWorker(container: Node, name: string): Symbol | undefined {
        return container.locals && container.locals.get(name);
    }
    /**
     * "Binds" JSDoc nodes in TypeScript code.
     * Since we will never create symbols for JSDoc, we just set parent pointers instead.
     */
    function setParentPointers(parent: Node, child: Node): void {
        child.parent = parent;
        forEachChild(child, grandchild => setParentPointers(child, grandchild));
    }
}
